﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using Vit.Common;
using Vit.Common.Helpers;
using Vit.Common.Extensions;
using Vit.Web.Models;
using Vit.Web.Providers;

namespace Vit.Web.Helpers
{
    public class MenuHelper
    {
        public static IEnumerable<NavigationItem> ParseMenuItems(IEnumerable<XElement> eles, Func<XElement, NavigationItem> parseItem)
        {
            var menus = HierarchyHelper.Build<XElement, NavigationItem>(eles,
             o =>
             {
                 return parseItem(o);
             },
             o => o.Elements("menu"),
             (p, c) => { p.Items.Add(c); c.Parent = p; });

            HierarchyHelper.Singlize<NavigationItem>(menus,
                o => o.Items,
                (a, b) => a.Name.ToLower() == b.Name.ToLower() && a.Url.ToLower() == b.Url.ToLower(),
                (p, c) => { p.Items.Add(c); c.Parent = p; },
                (list, o) => ((ICollection<NavigationItem>)list).Remove(o));

            return menus;
        }

        public static NavigationItem GetNavigationItemByName(IEnumerable<NavigationItem> items, string name)
        {
            var foundItems = HierarchyHelper.FindDescendants<NavigationItem>(items, o => o.Items, o => o.Name == name);
            return foundItems.FirstOrDefault();
        }

        public static NavigationItem GetNavigationItemByUrl(IEnumerable<NavigationItem> items, string url)
        {
            var foundItems = HierarchyHelper.FindDescendants<NavigationItem>(items, o => o.Items, o => o.Url == url);
            return foundItems.FirstOrDefault();
        }

        private static IQueryable<NavigationItem> GetCurrentCachedGroupMenu(string menuGroupName, Func<IQueryable<NavigationItem>> getItems)
        {
            string key = string.Format("CurrentMenu:{0}", menuGroupName);

            IQueryable<NavigationItem> menus = null;
            if (HttpContext.Current.Session[key] != null)
                menus = HttpContext.Current.Session[key] as IQueryable<NavigationItem>;
            if (menus == null || menus.Count() <= 0)
            {
                string userName = PortalContext.Current.User.Account;
                menus = getItems();
                if (menus == null)
                    menus = Enumerable.Empty<NavigationItem>().AsQueryable();
                menus = menus.Where(o => CanCurrentUserAccessMenuItem(o));
                HttpContext.Current.Session[key] = menus;
            }
            return menus;
        }

        public static IQueryable<NavigationItem> GetCurrentUserMenu()
        {
            string menuGroupName = PortalContext.Current.User.Profiles.GetProfileValue("MenuGroup", string.Empty);
            return GetCurrentCachedGroupMenu(menuGroupName,
                () =>
                Unity.Current.ResolveAny<INavigationProvider>().ForEach<INavigationProvider, IQueryable<NavigationItem>>(p => p.GetNavigationItemsByGroup(menuGroupName)));
        }

        public static NavigationItem GetCurrentMenuItem()
        {
            IEnumerable<NavigationItem> crumbleItems = GetCrumblePathItems(null);
            return crumbleItems == null || crumbleItems.Count() <= 0 ? null : crumbleItems.Last();
        }

        public static IEnumerable<NavigationItem> GetMenuItems(int level)
        {
            int i = 1;
            IEnumerable<NavigationItem> items = GetCurrentUserMenu();
            NavigationItem parent = null;
            IEnumerable<NavigationItem> crumbleItems = GetCrumblePathItems(null);

            while (i < level)
            {
                if (items.Count() > 0)
                {
                    parent = items.FirstOrDefault(o => crumbleItems.Any(it => it.Equals(o)));
                    if (parent == null)
                        parent = items.First();
                }
                if (parent != null)
                    items = parent.Items;
                i++;
            }
            return items;
        }

        public static bool IsSelected(NavigationItem item)
        {
            IEnumerable<NavigationItem> crumbleItems = GetCrumblePathItems(null);
            return crumbleItems.Any(o => o.Name == item.Name && o.Url == item.Url);
        }

        public static bool IsMenuMatch(NavigationItem item, string url)
        {
            if (string.IsNullOrWhiteSpace(item.SelectedPattern))
            {
                return item.Url.Equals(url, StringComparison.OrdinalIgnoreCase);
            }
            else
            {
                return Regex.IsMatch(url, item.SelectedPattern, RegexOptions.IgnoreCase);
            }
        }

        public static NavigationItem GetMenuItem(Func<NavigationItem, bool> predict)
        {
            var menu = GetCurrentUserMenu();
            if (menu != null)
            {
                Stack<NavigationItem> st = new Stack<NavigationItem>();
                foreach (var m in menu)
                    st.Push(m);

                while (st.Count > 0)
                {
                    var p = st.Pop();
                    if (predict(p))
                        return p;
                    foreach (var c in p.Items)
                        st.Push(c);
                }
            }
            return null;
        }

        public static IEnumerable<NavigationItem> GetCrumblePathItems(NavigationItem item)
        {
            if (item == null)
                item = MenuHelper.GetMenuItem(o => IsMenuMatch(o, HttpContext.Current.Request.Path));

            string key = string.Format("NavigationItemCrumblePath:{0}|{1}", item == null ? "NULL" : item.Name, item == null ? "NULL" : item.Url);
            if (HttpContext.Current.Session[key] != null)
                return HttpContext.Current.Session[key] as List<NavigationItem>;

            List<NavigationItem> items = new List<NavigationItem>();
            if (item == null)
            {
                var menu = GetCurrentUserMenu();
                if (menu != null)
                {
                    foreach (var m in menu)
                        items.Add(m);
                }
            }
            else
            {
                NavigationItem temp = item;
                while (temp != null)
                {
                    items.Add(temp);
                    temp = temp.Parent;
                }

                items.Reverse();
            }
            HttpContext.Current.Session[key] = items;
            return items;
        }

        public static bool CanCurrentUserAccessMenuItem(NavigationItem item)
        {
            return CanCurrentUserAccess(item.PermissionPattern);
        }

        public static bool CanCurrentUserAccess(string permissionPattern)
        {
            if (string.IsNullOrWhiteSpace(permissionPattern))
                return true;
            if (HttpContext.Current.Request.IsAuthenticated == false)
                return false;
            return Unity.Current.ResolveAny<IMembershipProvider>().ForEach<IMembershipProvider, bool>(p => p.CanUserAccess(PortalContext.Current.User.Account, permissionPattern));
        }
    }
}
